﻿/**
 * Copyright (C) 2012 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Spi;

namespace NGuice.Inject.Internal
{
    /// <summary>
    /// 非泛型的标记接口,C#中没有泛型通配符，使用此标记接口解决通配符泛型问题
    /// </summary>
    public interface ConstructionContext { }

    /// <summary>
    /// 一个依赖构造器的上下文。用于管理循环引用
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public sealed class ConstructionContext<T> : ConstructionContext
    {
        T currentReference;

        public T CurrentReference
        {
            get { return currentReference; }
            set { currentReference = value; }
        }

        public void RemoveCurrentReference()
        {
            this.currentReference = default(T);
        }

        bool constructing;

        public bool IsConstructing
        {
            get
            {
                return constructing;
            }
        }

        public void StartConstruction()
        {
            this.constructing = true;
        }

        public void FinishConstruction()
        {
            this.constructing = false;
            invocationHandlers = null;
        }

        List<DelegatingInvocationHandler<T>> invocationHandlers;

        public object CreateProxy(Errors errors, Type expectedType)
        {
            // TODO: if I create a proxy which implements all the interfaces of
            // the implementation type, I'll be able to get away with one proxy
            // instance (as opposed to one per caller).

            if (!expectedType.IsInterface)
            {
                throw errors.CannotSatisfyCircularDependency(expectedType).ToException();
            }

            if (invocationHandlers == null)
            {
                invocationHandlers = new List<DelegatingInvocationHandler<T>>();
            }

            DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<T>();
            invocationHandlers.Add(invocationHandler);

            //TODO:需要使用Emit相关类实现动态代理[为了尽快发布版本，Version1.0首先采用Castle DynamicProxy库实现，将来版本将逐步替换成Emit或cecil实现]
            //ClassLoader classLoader = BytecodeGen.getClassLoader(expectedType);
            //return expectedType.cast(Proxy.newProxyInstance(classLoader,
            //    new Class[] { expectedType, CircularDependencyProxy.class }, invocationHandler));
            return Proxy.NewProxyInstance(new Type[] { expectedType, typeof(CircularDependencyProxy) }, invocationHandler, BytecodeGen.GetLoader(expectedType));
        }

        public void SetProxyDelegates(T delegatee)
        {
            if (invocationHandlers != null)
            {
                foreach (DelegatingInvocationHandler<T> handler in invocationHandlers)
                {
                    handler.Delegatee = delegatee;
                }
            }
        }
    }
}
